import { encode } from "./base64.js";
import { encode_nums } from "./vlq.js";
import { Segment } from "./segment.js";

export class SourceMap {
  /**
   * @type {Segment[][]}
   */
  _mappings_raw = [];

  constructor(options) {
    this.version = options.version || 3;
    this.file = options.file || "";
    this.sources = options.sources || [];
    this.sourcesContent = options.sourcesContent || [];
    this.names = options.names || [];
  }

  /**
   * absolute position
   */
  addSegment(line, column, source, sourceLine, sourceColumn, name) {
    const segment = new Segment(column, source, sourceLine, sourceColumn, name);
    if (!this._mappings_raw[line]) {
      this._mappings_raw[line] = [segment];
    } else {
      this._mappings_raw[line].push(segment);
    }
  }

  // addSource(source, sourceContent) {
  //   this.sources.push(source);
  //   this.sourcesContent.push(sourceContent);
  // }

  // addName(name) {
  //   this.names.push(name);
  // }

  get mappings() {
    let result = "";

    let prev = new Segment(0, 0, 0, 0, 0);
    let prevName = 0;
    for (const line of this._mappings_raw) {
      const lineResult = [];
      for (const segment of line) {
        const offset = segment.offset(prev);

        // special for `.name`
        if (segment.name >= 0) {
          offset.name = segment.name - prevName;
          prevName = segment.name;
        }

        const encoded = encode_nums(offset.toArray());
        lineResult.push(encoded);

        prev = segment;
      }

      result += lineResult.join(",") + ";";
    }

    return result;
  }

  /**
   * to contain `.mappings`.
   * directly JSON.stringify() won't contain `.mappings`
   */
  toJSON() {
    return {
      version: this.version,
      file: this.file,
      sources: this.sources,
      sourcesContent: this.sourcesContent,
      names: this.names,
      mappings: this.mappings,
    };
  }

  toString(space = 0) {
    return JSON.stringify(this, null, space);
  }

  toURL() {
    return `data:application/json;charset=utf-8;base64,${encode(
      this.toString()
    )}`;
  }
}
